/*
 * File:   main.cpp
 * Author: Tomasz Gałaj i Damian Wojtas
 *
 * Created on 9 październik 2011, 17:03
 *
 */

#include <cstdlib>
#include <iostream>
#include <cmath>
#include <string>
#include <conio.h>

#include "menu.cpp"
#include "gnuplot_i.hpp"

#define GNUPLOT_PATH "C:/gnuplot/bin/"

using namespace std;

//Deklaracja tablicy oraz jej dlugosci ze współczynnikami wielomianu
//która jest wykorzystywana w funkcji horner
string wzory[] = {"x**3 - 5*x*x + 2*x + 8",
                  "sin(x)",
                  "(2**x) - 2",
                  "sin(2**x -2)",
                  "2**(sin(x)) -1"};
double tab_wspol[] = {1, -5, 2, 8}; //miejsca zerowe: -1, 2, 4
double wynik_b, wynik_s;
int dlugosc = 4;

double potega(double podstawa, int wykladnik)
{
    double wynik = 1;

    if(wykladnik == 0)
    {
        return 1;
    }

    for(int i = 0; i < wykladnik; i++)
    {
        wynik *= podstawa;
    }
    return wynik;
}


double horner (double tab_wspol[], double arg, int n) {
//Funkcja obliczajaca wartosc wielomianu
//za pomoca schematu hornera, za parametry przyjmuje
//tablice ze wspolczynnikami, argument oraz dlugosc
//tablicy wspolczynnikow (n)
    double wartosc = 0;

    for (int i = 0; i < n; i++) {
        if (i == 0) {
            wartosc += tab_wspol[i];
        } else {
            wartosc *= arg;
            wartosc += tab_wspol[i];
        }
    }

    return wartosc;
}

double f(int ktora, double x){
//Funkcja obliczajaca wartosci dla
//wybranej przez uzytkownika funkcji
//jako argumenty przyjmuje cyfre
//okreslajaca numer funkcji z menu
//oraz argument x, dla ktorego ma zostac policzona
//wartosc danej funkcji
double wynik;
    switch (ktora) {
    case 1:
        return horner(tab_wspol, x, dlugosc);
        break;
    case 2:
        return sin(x);
        break;
    case 3:
        return pow(2, x) - 2;
        break;
    case 4:
        return sin(pow(2,x) -2);
        break;
    case 5:
        return pow(2, sin(x))-1;
        break;
    }

    cout<<endl<<"Wystapil nieoczekiwany blad!"<<endl;
    return 0;
}

double sieczne(int ktora, double x1, double x2, double eps){

double x0;
int i = 0;
bool zmiana = false;

if ((f(ktora, x1) * f(ktora, x2)) > 0)
{
    cout<<"Zle punkty poczatkowe!\n";
    return NULL;
}

while(zmiana == false)
{
    x0 = x2 - (f(ktora, x2) * (x2 - x1)) / (f(ktora, x2) - f(ktora, x1));
    x1 = x2;
    x2 = x0;

    if (abs(f(ktora, x0)) < eps)
    {
        zmiana = true;
    }
    i++;

    if(i == 100) zmiana = true;
}
    cout << "sieczne ilosc iteracji: " << i<<endl;
    return x0;

}

double sieczne(int ktora, double x1, double x2, int ilosc_iteracji){
double x0, f1,f2;

f1 = f(ktora, x1); f2 = f(ktora, x2);

if ((f1 * f2) > 0)
{
    cout<<"Zle punkty poczatkowe!\n";
    return NULL;
}

while(ilosc_iteracji > 0)
{
    x0 = x2 - (f(ktora, x2) * (x2 - x1)) / (f(ktora, x2) - f(ktora, x1));
    x1 = x2;
    x2 = x0;

    ilosc_iteracji--;
    //cout << "x=" << x0 <<" y=" << f(ktora, x0)<<endl;
}
return x0;
}

double bisekcja(double a, double b, double eps, int ktora) {
//Funkcja obliczajaca miejsca zerowe
//funkcji metoda bisekcji (z epsilionem)
//a - lewy koniec przedzialu
//b - prawy koniec przedzialu
//eps - wartosc epsilion podawana przez uzytkownika
//ktora - cyfra okreslajaca numer funkcji z glownego menu
    double p;

    if (f(ktora, a) * f(ktora, b) > 0)
    {
        cout << "Ta funkcja nie ma miejsc zerowych w tym przedziale!\n";
        return NULL;
    }

    int i=0;

    do
    {
        i++;
        p = (a+b)/2;

        if (f(ktora, p) == 0)
        {
            cout << "bisekcja ilosc iteracji: " << i<<endl;
            return p;
        }

        if(f(ktora, a)*f(ktora, p) < 0)
        {
            b = p;
        } else
        {
            a = p;
        }
    } while ((b-a) > eps);
    cout << "bisekcja ilosc iteracji: " << i<<endl;
    return (a+b)/2;
}

double bisekcja(double a, double b, int iteracje, int ktora) {
//Funkcja obliczajaca miejsca zerowe
//funkcji metoda bisekcji (iteracyjnie)
//a - lewy koniec przedzialu
//b - prawy koniec przedzialu
//iteracje - ilosc iteracji podawana przez uzytkownika
//ktora - cyfra okreslajaca numer funkcji z glownego menu

    double p;

    if (f(ktora, a) * f(ktora, b) > 0)
    {
        cout << "Ta funkcja nie ma miejsc zerowych w tym przedziale!\n";
        return NULL;
    }

    int i = 0;

    do
    {
        p = (a+b)/2;

        if(f(ktora, p) == 0)
        {
            return p;
        }

        if(f(ktora, a)*f(ktora, p) < 0)
        {
            b = p;
        } else
        {
            a = p;
        }

        i++;
    } while (i < iteracje);
    return (a+b)/2;
}

void menuWyboruZakonczenia(double*a, double* b, int* kf){

    int choice;

    do{
    cout<<"Wybierz kryterium zatrzymania algorytmu: \n"
        <<"1. Wprowadzam E (epsilion)\n"
        <<"2. Wprowadzam liczbe iteracji\n\n";
    cin >> choice;
    }while(!(choice>=1 && choice<=2));

    if(choice==1)
    {
        double eps;
        cout << "Podaj E (epsilon): ";
        cin >> eps;
        wynik_b = bisekcja(*a, *b, eps, *kf);
        cout << "Wynik bisekcji: " << wynik_b << "\n";
        wynik_s = sieczne(*kf, *a, *b, eps);
        cout << "Wynik siecznych: " << wynik_s << "\n";
    }
    else
    {
        int ilosc_iteracji;
        cout << "Podaj liczbe iteracji: ";
        cin >> ilosc_iteracji;
        wynik_b = bisekcja(*a, *b, ilosc_iteracji, *kf);
        cout << "Wynik bisekcji: " << wynik_b << "\n";
        wynik_s = sieczne(*kf, *a, *b, ilosc_iteracji);
        cout << "Wynik siecznych: " << wynik_s << "\n";
    }
}

void rysujWykres(int kf, int left, int right)
{
      Gnuplot::set_GNUPlotPath( GNUPLOT_PATH );

      Gnuplot main_plot;

      // Podpisy na wykresie, żeby było wiadomo co na nim widać
      main_plot.set_title( "Metody numeryczne zadanie 1 - Bisekcja i metoda siecznych" );
      main_plot.set_xlabel( "X" );
      main_plot.set_ylabel( "Y" );

      // styl rysowania wykresu
      main_plot.set_style( "lines" );

      // siatka poprawia czytelność
      main_plot.set_grid();

      // zakres osi x
      main_plot.set_xrange( left , right );
      main_plot.set_yrange( -10, 10 );

      // funkcja do narysowania
      main_plot.plot_equation( wzory[kf-1], "Wzor: " + wzory[kf-1] ) ;

      // teraz narsujemy kilka punktów. Na początek zmiana stylu rysowania:
      main_plot.set_style( "points" );
      main_plot.set_pointsize( 2.0 );

      // Tworzenie danych do wykresu. Potrzebne są dwa wektory STL. Jeden opisuje
      // położenie punktow na osi X, drugi na osi Y. W rzeczywistym programie
      // dane nie będą oczywiście wpisywane na sztywno w kodzie, a wyliczane w oparciu
      // o parametry wprowadzone przez użytkownika.
      vector<double> x( 2 );
      x[ 0 ] = wynik_b;
      x[ 1 ] = wynik_s;

      vector<double> y ( 2 );
      y[ 0 ] =  0.0;
      y[ 1 ] = 0.0;

      main_plot.plot_xy( x, y, "Znalezione miejsce zerowe" );

      // czekamy na naciśniecie klawisza Enter
      cin.ignore();
      getchar();
}

int main() {
    //DEKLARACJA ZMIENNYCH
    double left, right;
    int kf; //która funcja
    //koniec DEKLARACJI ZMIENNYCH

    do
    {
        menuWyboruFunkcji(&kf);
        menuWyboruPrzedzialow(&left, &right);
        menuWyboruZakonczenia(&left, &right, &kf);
        rysujWykres(kf, left, right);
        cout<<"Nacisnij dowolny klawisz, aby kontynuowac lub ESC aby zakonczyc...\n";
    } while (getch() != 27);

    return 0;
}
